#!/bin/bash 
# 制作人：李凯 版本：最终版（注释版） 功能：实现一次性脱敏清洗 （可选字段）
# $1 输入源表名 $2 输出表名 $3及以后 字段-类型对（格式：字段名:脱敏类型，多个用空格分隔）
#运行前删除注释

#判断输入参数是否正确（至少需要源表、输出表、1个字段-类型对）
if [ $# -lt 3  ]; then 
    echo "字段个数不正确，请检查。正确格式：$0 源表名 输出表名 字段1:类型1 字段2:类型2 ..."
    exit 1
fi 

source_table=$1                     #来源表     加库名
output_table=$2                     #输出表名   加库名 
shift 2                             #调整位置 左移两位 -- 运行后 $3变$1
field_type_pairs=("$@")             #存储所有“字段:脱敏类型”对 --数组
current_date_str=$(date +%Y-%m-%d)  #分区字段

# 检查是否是“字段:类型”格式（要包含:，并且前后非空）
for pair in "${field_type_pairs[@]}"; do
#if正则判断
    if [[ ! "$pair" =~ ^[^:]+:[^:]+$ ]]; then
        echo "参数格式错误：$pair（应为：字段名:脱敏类型）"
        exit 1 #错一个就退出
    fi
done

#提取表字段 注意排除分区字段

Field_Str=$(hive -S -e "desc $source_table;" | awk '
    BEGIN { flag=1 } 
    /^# Partition Information/ { flag=0 }  
	#desc找第一列字段名  flag 区分 “非分区字段” 和 “分区字段“ ^排除分区字段  
	
    # 排除空行、注释行、etl_date字段（因目标表中是分区列）
	
    flag==1 && $1 !~ /^$/ && $1 !~ /^#/ && $1 != "etl_date" { print $1 }
' | tr '\n' ',' | sed 's/,$//')

# 把末尾的， 替换成空值   
# flag==1是非分区字段  $1 !~ /^$/  非空行 第一列  $1 !~ /^#/ 非 注释行 $1 != "etl_date"不是分区
#满足以上需求打印 第一列  ； 
#| tr '\n' ',' 把换行符变成逗号 因为awk每行输出一个 所以要把最后一行的逗号去掉


#if判断第一列的字段值是不是为空 为空报错
if [[ -z "$Field_Str" ]]; then  #-z 为空
    echo "错误：源表 $source_table 字段提取失败"
    exit 1
fi

# 将“字段:类型”对转化为关联数组（key=字段名，value=脱敏类型）
declare -A field_type_map  #声明关联数组
for pair in "${field_type_pairs[@]}"; do
    field=$(echo "$pair" | cut -d':' -f1)  #提取字段名 -d指定分隔符 cut以冒号切断 f1第一部分
    type=$(echo "$pair" | cut -d':' -f2)   #提取脱敏类型
    #判断字段是否存在于源表
    if [[ ! "$Field_Str" =~ "$field" ]]; then
        echo "错误：源表 $source_table 中不存在字段 $field"
        exit 1
    fi
    field_type_map["$field"]="$type"
done

#select子句

Select_Clause=""
IFS=',' read -ra FIELDS <<< "$Field_Str"  #<<<重定向 把第一列的字段
#变成字符串标准输出给用逗号做分隔符的命令 拆成数组

#for遍历数组（第一列）
for field in "${FIELDS[@]}"; do  
    #检查当前字段是否需要脱敏（是否在field_type_map中）
    if [[ -n "${field_type_map[$field]}" ]]; then
        target_type="${field_type_map[$field]}"
        logic=""  #初始化当前字段的脱敏逻辑

        #逻辑
        #电话脱敏
        if [[ "$target_type" == "phone" ]] ; then 
            logic="case when (nvl($field, '') = '') then '' #空值判断
            when (length($field) < 7) then $field    #电话位数判断 
            else concat(substr($field, 1, 3), repeat('*', length($field) - 6), substr($field, length($field) - 2, 3)) 
            end as $field"  # 用*填充 （用repeat替代lpad）
            
            
        #姓名脱敏

        elif [[ "$target_type" =~ ^(CUST_NAME|REMITTER_NAME|BENEFICIARY_NAME|UBO_NAME|NAME|ALIAS_NAME|TARGET_NAME)$ ]]; then 
            #姓名类型
            echo "匹配姓名字段 $field，执行脱敏"
            logic="case when (nvl($field, '') = '') then '' #空值判断
            when (length($field) >= 2) then concat(substr($field, 1, 1), repeat('*', length($field) - 1)) 
            #长度判断
            else $field  #小于两个不脱敏
            end as $field"

        elif [[ "$target_type" =~ ^(EMAIL|LOGIN_EMAIL|CONTACT_EMAIL)$ ]]; then 
            #邮箱类型
            echo "匹配邮箱字段 $field，执行脱敏"
            logic="case when (nvl($field, '') = '') then '' #空值判断
            when (instr($field, '@') > 0) then concat(repeat('*', instr($field, '@') - 1), substr($field, instr($field, '@'))) 
            #存在@符号时，@前全部替换为*，保留@及之后内容
            else $field  #不存在@符号不脱敏
            end as $field"
            
        elif [[ "$target_type" =~ ^(RESIDENT_ADDR|BIRTH_PLACE|ADDRESS|DOMICILE_ADDR)$ ]]; then 
            #地址类型（居住地/出生地等）
            echo "匹配地址字段 $field，执行脱敏"
            logic="case when (nvl($field, '') = '') then '' #空值判断
            when (length($field) >= 1) then concat(repeat('*', length($field) - 1), substr($field, -1)) 
            #保留最后1位（字或符号），前面用*替换
            else $field  #空字符串或异常长度不脱敏
            end as $field"

        elif [[ "$target_type" =~ ^(ID_NO|CERT_NO|UBO_ID_NO|LICENSE_NO)$ ]]; then 
            #证件号码类型（身份证、护照、营业执照等）
            echo "匹配证件号码字段 $field，执行脱敏"
            logic="case when (nvl($field, '') = '') then '' #空值判断
            when (length($field) >= 2) then concat(substr($field, 1, 1), repeat('*', length($field) - 2), substr($field, -1)) 
            #基础规则：保留前1位和后1位，中间用*替换（长度≥2时生效）
            else $field  #长度不足2位不脱敏
            end as $field"

        else 
            echo "不支持的脱敏类型：$target_type（字段：$field）"  
            exit 1 
        fi

        Select_Clause="${Select_Clause}${logic},"  #运行select子句（脱敏字段）
    else
        Select_Clause="${Select_Clause}${field},"  #非脱敏字段直接保留
    fi
done

Select_Clause=$(echo "$Select_Clause" | sed 's/,$//')  #去除逗号

#HQL
hql_str="
set hive.exec.dynamic.partition.mode=nonstrict; #hive动态分区模式
INSERT INTO TABLE $output_table    #insert into 追加 overwrite覆盖
PARTITION (etl_date)    #分区字段
SELECT $Select_Clause, '$current_date_str' AS etl_date  #所有源表需要的非分区字段 （已经脱敏） 
#加上分区字段名
FROM $source_table;  #源表
"



echo "================ 开始执行脱敏 ================"
echo "源表名：$source_table"
echo "待脱敏字段及类型：${field_type_pairs[*]}"
echo "输出表名：$output_table"
echo "执行的HQL语句：$hql_str"
echo "=============================================="


#调用hive执行sql
hive -e "$hql_str"

# 检查是否成功
if [ $? -eq 0 ]; then 
    echo "脱敏成功"
else 
    echo "脱敏失败，请检查"
    exit 1
fi